package model.entity.container;

import model.exceptions.InvalidNameException;
import model.exceptions.NullContainerException;
import common.Result;

// @formatter:off
/**
 * Container to organize similar products into groups.
 * 
 * @author Trevor Dixon, Ryan Cheatham
 * 
 *         {@invariant getThreeMonthSupply() > 0} {@invariant container != null}
 */
// @formatter:on
public class ProductGroup extends ProductContainer
{
  private static final long serialVersionUID = 2857664814855087394L;

  private ProductContainer  container;
  private int               threeMonthSupply;

  // @formatter:off
  /**
   * Creates a product group with the given name, container, and three month supply.
   * 
   * @param name
   *          User-defined name of the group. Must be unique among all
   *          <code>ProductContainer</code>s in the parent container.
   * 
   * @param container
   *          The <code>ProductContainer</code> that is the parent of this group.
   * 
   * @param threeMonthSupply
   *          The number of items needed to constitute a "three month supply" for this group.
   * 
   *          {@pre name != null} {@pre name.length() > 0} {@pre container != null} {@pre
   *          threeMonthSupply >= 0}
   * 
   *          {@post getName() == name} {@post getContainer() == container} {@post
   *          getThreeMonthSupply() == threeMonthSupply}
   * @throws InvalidNameException
   * @throws NullContainerException 
   */
  // @formatter:on
  public ProductGroup(String name, ProductContainer container, int threeMonthSupply)
      throws InvalidNameException, NullContainerException
  {
    super(name);

    if (container == null)
    {
      throw new NullContainerException("Container must not be null.");
    }

    if (threeMonthSupply < 0)
    {
      throw new IllegalArgumentException("Three month supply must not be less than 0.");
    }

    this.container = container;
    this.threeMonthSupply = threeMonthSupply;
  }

  /**
   * @return The number of items needed to constitute a "three month supply" for this group.
   */
  public int getThreeMonthSupply()
  {
    return threeMonthSupply;
  }

  /**
   * @return The parent <code>ProductContainer</code>.
   */
  public ProductContainer getContainer()
  {
    return container;
  }

  /**
   * Validates whether the container of this product group can be changed to the given container.
   * 
   * @param container
   *          the container to validate as a possible new container of this product group.
   * @return a result, indicating whether the container can be set as this product group's
   *         container
   * 
   *         {@pre container != null}
   */
  public Result validateUpdateContainer(ProductContainer container)
  {
    if (container == null)
    {
      return new Result(false, "Container must not be null");
    }
    else if (this.container.equals(container))
    {
      return new Result(false, "This container is already the parent for this product group.");
    }

    return new Result(true);
  }

  // @formatter:off
  /**
   * Attempts to move this product group to the given container.
   * 
   * @param container
   *          The <code>ProductContainer</code> to move this group to.
   * @return a result, indicating whether this operation was successful or not.
   * 
   *         {@pre container != null}
   * 
   *         {@post getContainer() == container}
   */
  // @formatter:on
  public Result updateContainer(ProductContainer container)
  {
    Result canUpdate = validateUpdateContainer(container);
    if (!canUpdate.getStatus())
    {
      return canUpdate;
    }

    this.container = container;
    return new Result(true);
  }

  @Override
  public int hashCode()
  {
    final int prime = 31;
    int result = super.hashCode();
    result = prime * result + ((container == null) ? 0 : container.hashCode());
    result = prime * result + threeMonthSupply;
    return result;
  }

  @Override
  public boolean equals(Object obj)
  {
    if (this == obj)
      return true;
    if (!super.equals(obj))
      return false;
    if (!(obj instanceof ProductGroup))
      return false;
    ProductGroup other = (ProductGroup) obj;
    if (container == null)
    {
      if (other.container != null)
        return false;
    }
    else if (!container.equals(other.container))
      return false;
    return true;
  }
}
